import { EventEmitter } from "../../base/events"
import { ref } from "vue"
const fs = (window as any).require("fs")
const path = (window as any).require("path")

const appdir = path.dirname((window as any).process.mainModule.filename)

const MaxAudioFrame = 100

const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const lookup = typeof Uint8Array === 'undefined' ? [] : new Uint8Array(256);
for (let i = 0; i < chars.length; i++) {
    lookup[chars.charCodeAt(i)] = i;
}

export const base64Decode = (base64: string): ArrayBuffer => {
    let bufferLength = base64.length * 0.75,
        len = base64.length,
        i,
        p = 0,
        encoded1,
        encoded2,
        encoded3,
        encoded4;

    if (base64[base64.length - 1] === '=') {
        bufferLength--;
        if (base64[base64.length - 2] === '=') {
            bufferLength--;
        }
    }

    const arraybuffer = new ArrayBuffer(bufferLength),
        bytes = new Uint8Array(arraybuffer);

    for (i = 0; i < len; i += 4) {
        encoded1 = lookup[base64.charCodeAt(i)];
        encoded2 = lookup[base64.charCodeAt(i + 1)];
        encoded3 = lookup[base64.charCodeAt(i + 2)];
        encoded4 = lookup[base64.charCodeAt(i + 3)];

        bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
        bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
        bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
    }

    return arraybuffer;
}


interface RecordedFrames {
    frames: [ArrayBuffer, number, number] [] ,
    type: string ,
    desc: string
}

export class FrameBuffer extends EventEmitter {

    private videoFrame: [ArrayBuffer, number, number] | null = null
    private audioFrames: ArrayBuffer[] = []

    public acceptSource = 'screen'

    public open = true

    // 录播 (保存在内存中)
    public recording = false
    public recordedFrames: RecordedFrames[] = []
    public replayRecordIdx = -1
    public replayFrmIdx = 0

    // 保存为帧序列 (保存为文件)
    public savingFrames = false
    public savedFrameNum = 0
    public unsaveFrameList: string[] = []


    // 当 needKeyframe=true 时，
    // source 发送一帧 关键帧（完整的画面）, 并由 source 负责清理这一属性 (needKeyframe=false)
    public needKeyframe = true 

    acceptTest(source:string) {
        return this.open && source==this.acceptSource
    }

    lastRecorded(): RecordedFrames {
        return this.recordedFrames[this.recordedFrames.length-1]
    }
    lastRecordedFrames(): [ArrayBuffer, number, number][] {
        return this.lastRecorded().frames
    }

    pushVideoFrame(source:string, frame: ArrayBuffer, x:number, y:number) {
        if(this.open && source!=this.acceptSource) {
            return
        }
        if(this.recording) {
            this.lastRecordedFrames().push([frame, x, y])
        } else {
            this.videoFrame = [frame,x,y]
            this.emit("newframe")
        }
    }

    pushVideoFrameBase64(source:string, urlData: string, x:number, y:number) {
        if(this.open && source!=this.acceptSource) {
            return false
        }
        
        urlData = urlData.replace(/^data:image\/jpeg;base64,/, '')
        let ab = base64Decode(urlData)

        if(this.recording) {
            this.lastRecordedFrames().push([ab, x, y])
        } else {
            this.videoFrame = [ab,x,y]
            this.emit("newframe")
        }

        return true
    }

    startRecord(type:string) {
        if(this.recording) {
            return
        }
        this.recordedFrames.push({frames:[], type, desc:''})
        this.recording = true
    }

    stopRecord() {
        this.recording = false
    }

    replay(recordIdx:number) {
        this.replayRecordIdx = recordIdx
        this.replayFrmIdx = 0
    }
    stopReplay() {
        this.replayRecordIdx = -1
    }


    
    startSaveFrames() {
        if(this.savingFrames) {
            return
        }
        try{ fs.mkdirSync(appdir+"/data/") }catch(e){}
        try{ fs.mkdirSync(appdir+"/data/frames") }catch(e){}

        this.savingFrames = true
        this.savedFrameNum = 1
        this.unsaveFrameList.length = 0
    }

    stopSaveFrames() {
        this.savingFrames = false

        for(let urlData of this.unsaveFrameList) {
            urlData = urlData.replace(/^data:image\/jpeg;base64,/, '')
            let ab = base64Decode(urlData)
    
            fs.writeFileSync(appdir+"/data/frames/frame-"+(this.savedFrameNum++).toString().padStart(6,'0')+".jpg", new Uint8Array(ab))
        }

        this.unsaveFrameList.length = 0
    }

    saveOneFrame(urlData:string) {

        this.unsaveFrameList.push(urlData)

        // let urlData = canvas.toDataURL("image/jpeg", 100)
    }

    popVideoFrame(): [ArrayBuffer|null, number, number] {
        if(this.replayRecordIdx>-1) {
            if(!this.recordedFrames.length || !this.recordedFrames[this.replayRecordIdx]?.frames[this.replayFrmIdx]) {
                return [null,0,0]
            }
            
            let output = this.recordedFrames[this.replayRecordIdx].frames[this.replayFrmIdx]

            this.replayFrmIdx++
            if(this.replayFrmIdx>=this.recordedFrames[this.replayRecordIdx].frames.length) {
                this.replayFrmIdx = 0
            }

            return output
        }

        else {
            let frame = this.videoFrame
            this.videoFrame = null
            return frame || [null,0,0]
        }
    }

    nextFrame(): Promise<[ArrayBuffer|null, number, number]> {
        return new Promise((resolve) => {
            this.once("newframe", ()=>{
                resolve(this.popVideoFrame())
            })
        })
    }

    isFull() {
        return !!this.videoFrame && !this.recording
    }
    
    pushAudioFrame(source:string, data: ArrayBuffer) {
        if(!this.open) {
            return
        }
        if(source!=this.acceptSource) {
            return
        }

        this.audioFrames.push(data)
        
        let over = this.audioFrames.length - MaxAudioFrame
        if(over) {
            console.log("drop",over)
            this.audioFrames.splice(0,over)
        }
    }
    popAudioFrame(): ArrayBuffer|null {
        return this.audioFrames.shift() || null
    }
    takeAudioFrames(): ArrayBuffer[] {
        let frames = [... this.audioFrames]
        this.audioFrames.length = 0
        return frames
    }

    hasAudioFrames() {
        return !!this.audioFrames.length
    }

}